package minesweeper;

import java.awt.GridLayout;
import java.awt.Point;
import java.util.Random;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

//Idea: Game -> Minefield
//This class if a JPanel, which is added to the MinesweeperGUI
public class Minefield extends JPanel {

    //holds all fields
    private Field[][] fields;
    private Resources res;
    
    private MinesweeperGUI minegui;
    private Minefield newgame;
    //the number of mines on the minefield
    private final int mines;
    //number of horizontal fields
    private final int width;
    //number of vertical fields
    private final int height;

    public Minefield(int width, int height) throws Exception {
        super(new GridLayout(height, width));
        if(height > 100 || height < 2)
        {
            throw new Exception("Height darf nicht größer 100 oder kleiner 2 sein!");
        }
        if(width > 100 || width < 2)
        {
            throw new Exception("Width darf nicht größer 100 oder kleiner 2 sein!");
        }
        fields = new Field[width][height];
        res = new Resources();
        this.width = width;
        this.height = height;
        mines = calcMines();
        initGame();
    }

    //calculates the number of mines, based on the size of the minefield
    private int calcMines() {
        return ((width * height) / 10) + 3;
    }

    //initialises the game
    private void initGame() {
        //temp array to store all the fields with mines on them
        Point[] minePoints = new Point[mines];
        Random rand = new Random();
        int x = 0;
        int y = 0;

        //generates "mines"-mines (Ex: 12-mines)
        for (int i = 0; i < mines; i++) {
            do {
                x = rand.nextInt(width);
                y = rand.nextInt(height);
                //checks if the field is already a mine
            } while (fields[x][y] != null);
            //creates a new field with a mine on it
            fields[x][y] = new Field(Field.MINE, res, this, x, y);
            //stores the coordinates of the new mine in the temp array
            minePoints[i] = new Point(x, y);
        }

        //initialises the other fields
        for (int xT = 0; xT < width; xT++) {
            for (int yT = 0; yT < height; yT++) {
                //checks if the field is already initialised (is a mine)
                if (fields[xT][yT] == null) {
                    fields[xT][yT] = new Field(Field.EMPTY, res, this, xT, yT);
                }
                //adds the field to the minefield
                this.add(fields[xT][yT]);
            }
        }

        Point p = null;
        int xT, yT;
        //runs through all mines and updates the numbers of nearby mines of
        //the other fields
        for (int i = 0; i < mines; i++) {
            p = minePoints[i];
            x = p.x;
            y = p.y;

            //left
            xT = x - 1;
            yT = y;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //left down
            xT = x - 1;
            yT = y + 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //down
            xT = x;
            yT = y + 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //right down
            xT = x + 1;
            yT = y + 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //right
            xT = x + 1;
            yT = y;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //right up
            xT = x + 1;
            yT = y - 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //up
            xT = x;
            yT = y - 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }

            //left up
            xT = x - 1;
            yT = y - 1;
            if (isValidField(xT, yT)) {
                fields[xT][yT].increaseNearbyMineCount();
            }
        }


    }

    public int checkClosed(Field f) {
        repaint();
        Status closedchecks[] = new Status[4];// where something is closed index 0=left, index 1=up, index 2=right, index 3=down
        
        int x1 = f.getX1();
        int y1 = f.getY1();

        if (x1 == 0 && y1 != 0 && y1 != height - 1) {
            closedchecks[0] = Status.CLOSED;
            closedchecks[1] = up(x1, y1);
            closedchecks[2] = right(x1, y1);
            closedchecks[3] = down(x1, y1);

            return getClosed(closedchecks);
        }
        if (y1 == 0 && x1 != 0 && x1 != width - 1) {
            closedchecks[0] = left(x1, y1);
            closedchecks[1] = Status.CLOSED;
            closedchecks[2] = right(x1, y1);
            closedchecks[3] = down(x1, y1);

            return getClosed(closedchecks);
        }
        if (x1 == width - 1 && y1 != 0 && y1 != height - 1) {
            closedchecks[0] = left(x1, y1);
            closedchecks[1] = up(x1, y1);
            closedchecks[2] = Status.CLOSED;
            closedchecks[3] = down(x1, y1);

            return getClosed(closedchecks);
        }
        if (y1 == height - 1 && x1 != 0 && x1 != width - 1) {
            closedchecks[0] = left(x1, y1);
            closedchecks[1] = up(x1, y1);
            closedchecks[2] = right(x1, y1);
            closedchecks[3] = Status.CLOSED;

            return getClosed(closedchecks);
        }
        if (x1 == 0 && y1 == 0) {
            closedchecks[0] = Status.CLOSED;
            closedchecks[1] = Status.CLOSED;
            closedchecks[2] = right(x1, y1);
            closedchecks[3] = down(x1, y1);

            return getClosed(closedchecks);
        }
        if (x1 == 0 && y1 == height - 1) {
            closedchecks[0] = Status.CLOSED;
            closedchecks[1] = up(x1, y1);
            closedchecks[2] = right(x1, y1);
            closedchecks[3] = Status.CLOSED;

            return getClosed(closedchecks);
        }
        if (x1 == width - 1 && y1 == 0) {
            closedchecks[0] = left(x1, y1);
            closedchecks[1] = Status.CLOSED;
            closedchecks[2] = Status.CLOSED;
            closedchecks[3] = down(x1, y1);

            return getClosed(closedchecks);
        }
        if (x1 == width - 1 && y1 == height - 1) {
            closedchecks[0] = left(x1, y1);
            closedchecks[1] = up(x1, y1);
            closedchecks[2] = Status.CLOSED;
            closedchecks[3] = Status.CLOSED;

            return getClosed(closedchecks);
        }

        closedchecks[0] = left(x1, y1);
        closedchecks[1] = up(x1, y1);
        closedchecks[2] = right(x1, y1);
        closedchecks[3] = down(x1, y1);

        return getClosed(closedchecks);
    }

    private int getClosed(Status[] cc)//cc=closedchecks
    {
        if(cc[0]==Status.OPEN||cc[1]==Status.OPEN||cc[2]==Status.OPEN||cc[3]==Status.OPEN)
        System.out.println(cc[0].toString()+cc[1].toString()+cc[2].toString()+cc[3].toString());
        if (cc[0] == Status.OPEN && cc[1] == Status.OPEN && cc[2] == Status.OPEN && cc[3] == Status.OPEN) {
            return Field.CLOSED;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.OPEN && cc[2] == Status.OPEN && cc[3] == Status.OPEN) {
            return Field.CLOSEDLEFT;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.CLOSED && cc[2] == Status.OPEN && cc[3] == Status.OPEN) {
            return Field.CLOSEDLEFTUP;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.CLOSED && cc[2] == Status.CLOSED && cc[3] == Status.OPEN) {
            return Field.CLOSEDLEFTUPRIGHT;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.CLOSED && cc[2] == Status.OPEN && cc[3] == Status.CLOSED) {
            return Field.CLOSEDLEFTUPDOWN;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.OPEN && cc[2] == Status.CLOSED && cc[3] == Status.OPEN) {
            return Field.CLOSEDLEFTRIGHT;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.OPEN && cc[2] == Status.CLOSED && cc[3] == Status.CLOSED) {
            return Field.CLOSEDLEFTRIGHTDOWN;
        }
        if (cc[0] == Status.CLOSED && cc[1] == Status.OPEN && cc[2] == Status.OPEN && cc[3] == Status.CLOSED) {
            return Field.CLOSEDLEFTDOWN;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.CLOSED && cc[2] == Status.OPEN && cc[3] == Status.OPEN) {
            return Field.CLOSEDUP;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.CLOSED && cc[2] == Status.CLOSED && cc[3] == Status.OPEN) {
            return Field.CLOSEDUPRIGHT;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.CLOSED && cc[2] == Status.OPEN && cc[3] == Status.CLOSED) {
            return Field.CLOSEDUPDOWN;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.CLOSED && cc[2] == Status.CLOSED && cc[3] == Status.CLOSED) {
            return Field.CLOSEDUPRIGHTDOWN;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.OPEN && cc[2] == Status.CLOSED && cc[3] == Status.OPEN) {
            return Field.CLOSEDRIGHT;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.OPEN && cc[2] == Status.CLOSED && cc[3] == Status.CLOSED) {
            return Field.CLOSEDRIGHTDOWN;
        }
        if (cc[0] == Status.OPEN && cc[1] == Status.OPEN && cc[2] == Status.OPEN && cc[3] == Status.CLOSED) {
            return Field.CLOSEDDOWN;
        }

        return Field.CLOSEDLEFTUPRIGHTDOWN;

    }

    private Status left(int x1, int y1) {
        Field left = fields[x1 - 1][y1];

        return left.getStatus();

    }

    private Status up(int x1, int y1) {
        Field up = fields[x1][y1 - 1];
        return up.getStatus();
    }

    private Status right(int x1, int y1) {
        Field right = fields[x1 + 1][y1];
        return right.getStatus();
    }

    private Status down(int x1, int y1) {
        Field down = fields[x1][y1 + 1];
        return down.getStatus();
    }

    //returns if the field at the coordinates "x" and "y" is a existing field
    //and if the field is a mine
    private boolean isValidField(int x, int y) {
        if (x >= 0 && x < width && y >= 0 && y < height) {
            if (!fields[x][y].isMine()) {
                return true;
            }
        }
        return false;
    }

    public void mineHit() {
        openAllFields();
        JOptionPane.showMessageDialog(this, "Game Over!");
        if(JOptionPane.showConfirmDialog(this, "Neues Spiel beginnen?","Neues Spiel",JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION)
        {
            //ToDo Start New Game
        }
    }

    private void openAllFields() {
        for (int xT = 0; xT < width; xT++) {
            for (int yT = 0; yT < height; yT++) {
                fields[xT][yT].openField();
            }
        }
        repaint();
    }
}